#ifndef __M_POOL_H__
#define __M_POOL_H__

#include <iostream>
#include <mutex>
#include <atomic>
#include <condition_variable>
#include <vector>
#include <functional>
#include <thread>
#include <future>

namespace mq{
    
class threadpool{
public:

using ptr = std::shared_ptr<threadpool>;
    threadpool(int thr_count = 1):_stop(false){
        //创建对应的线程个数
        for(int i = 0 ; i < thr_count ;i++){
            _threads.emplace_back(std::thread(&threadpool::entry,this));//注意函数的 取地址 
            //线程的初始化构造函数：会自动执行entry，参数就是后面的args
        }
    }

    ~threadpool(){
        stop();
    }
    void stop(){
        if(_stop == true) return;//防止重复
        _stop = true;//设置结束标志
        //等待所有线程
        _cv.notify_all();//唤醒所有工作线程，P操作

        for(auto& thread : _threads){
            thread.join();
        }
        //将条件变量设为
    }

    template<typename F,typename ...Args>//F：函数、Args：参数
    auto push(F &&func,Args && ...args) -> std::future<decltype(func(args...))>
    {
        //推导出函数的返回值类型，同样使用decltype
        using return_type = decltype(func(args...));

        //使用bind函数适配器，提前将参数进行绑定，这样就能剩去参数的传递
        auto tmp_func = std::bind(std::forward<F>(func),std::forward<Args>(args)...);//注意此处要把...参数包放到外面
//其中使用到了 forward 进行完美转发，防止右值引用被当成左值进行传递

        //使用智能指针将，packaged_task任务包进行封装，并且把func执行的函数给到任务包，此处传递进行封装后的函数tmp_func
        auto task = std::make_shared<std::packaged_task<return_type()>>(tmp_func);

        std::future<return_type> fu = task->get_future();//获取关联的future对象
        //构造匿名lambda表达式进行捕获对象，并执行
        //对于共用成员，需要加锁处理：
        {
            //加锁方法使用 unique_lock，具体如下：
            std::unique_lock<std::mutex> lock(_mutex);//加锁完成！
            //进行异步操作
            //3. 将构造出来的匿名函数对象，抛入到任务池中
            _taskpool.push_back([task](){
                (*task)();//任务是执行该函数
            });
            _cv.notify_one();//唤醒线程
        }
        return fu; //返回future对象       
    }


    using Factor = std::function<void(void)>;
private:
//线程的入口内部不断的从任务池中取出任务进行执行：
    void entry(){
        while(!_stop){
            std::vector<Factor> tmp_taskpool;
            //1. cv 进行等待，等待任务到来，也就是stop结束标志被置位 或者 任务池为空
            {//加锁的作用域：
                std::unique_lock<std::mutex> lock(_mutex);
                _cv.wait(lock,[this](){
                    //唤醒条件
                    return _stop || !_taskpool.empty();
                });
                //唤醒后执行，但这样只能执行一个，所以在作用域外创建局部任务池
                tmp_taskpool.swap(_taskpool);//与全局的任务池交换，获取所有任务
            }
            //遍历执行任务
            for(auto& task : tmp_taskpool){
                task();//执行任务
            }
        }
     }
private:
    //1. 互斥锁
    std::mutex _mutex;
    //2. 条件变量
    std::condition_variable _cv;
    //3. 执行的任务池
    std::vector<Factor> _taskpool;
    //4. 线程池
    std::vector<std::thread> _threads;
    //5. 结束标志
    std::atomic<bool> _stop;
};
}

#endif